<script lang="ts">
  import {
    list_llm_provider,
    type LlmProviderInfo,
  } from "$lib/api/ai_provider/ai_provider";
  import { list_llm_model, type LlmModelInfo } from "$lib/api/ai_provider/llm";
  import { add_llm, update_llm, type LlmInfo } from "$lib/api/board/board";
  import { boardStore } from "$lib/stores/board.svelte";
  import { userStore } from "$lib/stores/user.svelte";
  import { request } from "$lib/utils/request";
  import { Modal } from "@skeletonlabs/skeleton-svelte";
  import type { Store as ToastStore } from "@zag-js/toast";
  import { X } from "lucide-svelte";
  import { getContext } from "svelte";

  const toast: ToastStore = getContext("toast");

  interface ModalProps {
    llmInfo?: LlmInfo;
    onClose: () => void;
  }
  let props: ModalProps = $props();

  let providerList = $state([] as LlmProviderInfo[]);
  let curProviderId = $state("");

  let modelList = $state([] as LlmModelInfo[]);

  let name = $state(props.llmInfo?.basic_info.name ?? "");
  let apiKey = $state(props.llmInfo?.basic_info.api_key ?? "");
  let apiAddr = $state(props.llmInfo?.basic_info.api_addr ?? "");
  let needApiKey = $state(false);
  let systemPrompt = $state(
    props.llmInfo?.basic_info.system_prompt ?? "你是一个有用的智能助手"
  );
  let defaultModelName = $state(
    props.llmInfo?.basic_info.default_model_name ?? ""
  );

  let valid = $derived(
    name.trim() != "" &&
      (needApiKey == false || (needApiKey && apiKey.trim() != "")) &&
      (apiAddr.trim().startsWith("http://") ||
        apiAddr.trim().startsWith("https://")) &&
      defaultModelName != ""
  );

  async function loadProviderList() {
    if (props.llmInfo != undefined) {
      return;
    }
    const res = await list_llm_provider({});
    providerList = res.provider_list;
    if (res.provider_list.length > 0) {
      curProviderId = res.provider_list[0].provider_id;
      name = res.provider_list[0].name;
      apiAddr = res.provider_list[0].default_addr;
      needApiKey = res.provider_list[0].need_api_key;
      defaultModelName = "";
    }
  }

  loadProviderList();

  async function addLlm() {
    await request(
      add_llm({
        session_id: userStore.userSession,
        board_id: boardStore.curBoardId,
        basic_info: {
          name: name.trim(),
          api_key: apiKey.trim(),
          api_addr: apiAddr.trim(),
          system_prompt: systemPrompt,
          default_model_name: defaultModelName.trim(),
        },
      })
    );
    toast.create({
      title: "成功",
      description: "增加大模型配置成功!",
      type: "success",
    });
    props.onClose();
  }

  async function updateLlm() {
    if (props.llmInfo == undefined) {
      return;
    }
    await request(
      update_llm({
        session_id: userStore.userSession,
        board_id: boardStore.curBoardId,
        llm_id: props.llmInfo.llm_id,
        basic_info: {
          name: name.trim(),
          api_key: apiKey.trim(),
          api_addr: apiAddr.trim(),
          system_prompt: systemPrompt,
          default_model_name: defaultModelName.trim(),
        },
      })
    );
    toast.create({
      title: "成功",
      description: "修改大模型配置成功!",
      type: "success",
    });
    props.onClose();
  }

  async function loadModelList() {
    try {
      const res = await list_llm_model(apiAddr.trim(), apiKey.trim());
      if (Array.isArray(res)) {
        modelList = res;
        if (res.length > 0) {
          defaultModelName = modelList[0].id;
        }
      }
    } catch (e) {
      console.log(e);
    }
  }

  $effect(() => {
    if (curProviderId != "" && apiAddr.trim() != "") {
      if (!needApiKey) {
        loadModelList();
      } else if (needApiKey && apiKey.trim() != "") {
        loadModelList();
      }
    } else if (props.llmInfo != undefined) {
      loadModelList();
    }
  });
</script>

<Modal
  open={true}
  triggerBase="btn preset-tonal"
  contentBase="card bg-surface-200-800 p-4 space-y-4 shadow-xl w-[500px]"
  backdropClasses="backdrop-blur-xs"
  backdropBase="backdrop-none"
  onOpenChange={(ev) => {
    if (!ev.open) {
      props.onClose();
    }
  }}
>
  {#snippet content()}
    <header class="flex justify-between">
      <h4 class="h4">
        {props.llmInfo == undefined ? "增加大模型" : "修改大模型"}
      </h4>
      <button
        type="button"
        class="btn preset-ghost-primary-500"
        onclick={() => props.onClose()}
      >
        <X />
      </button>
    </header>

    <main class="flex flex-col gap-2">
      {#if props.llmInfo == undefined}
        <label
          class="label border-[1px] relative p-2 border-surface-300-700 mt-2"
        >
          <span
            class="label-text absolute -top-[10px] left-[10px] bg-surface-200-800 pl-2 pr-2"
            >模型供应商</span
          >
          <select
            class="select rounded-none bg-surface-100-900"
            value={curProviderId}
            onchange={(e) => {
              const tmpProvider = providerList.find(
                (item) => item.provider_id == e.currentTarget.value
              );
              if (tmpProvider != undefined) {
                name = tmpProvider.name;
                apiAddr = tmpProvider.default_addr;
                needApiKey = tmpProvider.need_api_key;
                defaultModelName = "";
              }
              curProviderId = e.currentTarget.value;
            }}
          >
            {#each providerList as provider}
              <option value={provider.provider_id}>
                {provider.name}
              </option>
            {/each}
          </select>
        </label>
      {/if}

      <label
        class="label border-[1px] relative p-2 border-surface-300-700 mt-2"
      >
        <span
          class="label-text absolute -top-[10px] left-[10px] bg-surface-200-800 pl-2 pr-2"
          >名称</span
        >
        <input
          class="input rounded-none bg-surface-100-900"
          type="text"
          placeholder="请输入名称"
          bind:value={name}
        />
      </label>

      <label
        class="label border-[1px] relative p-2 border-surface-300-700 mt-2"
      >
        <span
          class="label-text absolute -top-[10px] left-[10px] bg-surface-200-800 pl-2 pr-2"
          >Api地址</span
        >
        <input
          class="input rounded-none bg-surface-100-900 {apiAddr
            .trim()
            .startsWith('http://') || apiAddr.trim().startsWith('https://')
            ? ''
            : 'ring-error-800-200'}"
          type="text"
          placeholder="请输入Api地址"
          bind:value={apiAddr}
        />
      </label>

      {#if needApiKey}
        <label
          class="label border-[1px] relative p-2 border-surface-300-700 mt-2"
        >
          <span
            class="label-text absolute -top-[10px] left-[10px] bg-surface-200-800 pl-2 pr-2"
            >Api密钥</span
          >
          <input
            class="input rounded-none bg-surface-100-900 {apiAddr
              .trim()
              .startsWith('http://') || apiAddr.trim().startsWith('https://')
              ? ''
              : 'ring-error-800-200'}"
            type="text"
            placeholder="请输入Api密钥"
            bind:value={apiKey}
          />
        </label>
      {/if}

      {#if modelList.length > 0}
        <label
          class="label border-[1px] relative p-2 border-surface-300-700 mt-2"
        >
          <span
            class="label-text absolute -top-[10px] left-[10px] bg-surface-200-800 pl-2 pr-2"
            >默认模型</span
          >
          <select
            class="select rounded-none bg-surface-100-900"
            bind:value={defaultModelName}
          >
            {#each modelList as model}
              <option value={model.id}>
                {model.id}
              </option>
            {/each}
          </select>
        </label>
      {/if}

      <label
        class="label border-[1px] relative p-2 border-surface-300-700 mt-2"
      >
        <span
          class="label-text absolute -top-[10px] left-[10px] bg-surface-200-800 pl-2 pr-2"
          >系统提示词</span
        >
        <textarea
          class="textarea bg-surface-100-900"
          rows="4"
          placeholder="请输入系统提示词"
          bind:value={systemPrompt}
        ></textarea>
      </label>
    </main>

    <footer class="flex justify-end gap-4">
      <button
        type="button"
        class="btn preset-tonal"
        onclick={() => props.onClose()}>取消</button
      >
      <button
        type="button"
        class="btn preset-filled-primary-900-100"
        disabled={!valid}
        onclick={() => {
          if (props.llmInfo == undefined) {
            addLlm();
          } else {
            updateLlm();
          }
        }}>{props.llmInfo == undefined ? "增加" : "修改"}</button
      >
    </footer>
  {/snippet}
</Modal>
